#pragma once

#include <random>

namespace mdtx::online
{
    namespace detail
    {
        /**
     * @brief Update xorshift+ state
     *
     * @param a state a
     * @param b state b
     * @return uint64_t
     */
        inline uint64_t xorshift128p(uint64_t &a, uint64_t &b)
        {
            uint64_t t = a;
            uint64_t const s = b;
            a = s;
            t ^= t << 23;       // a
            t ^= t >> 17;       // b
            t ^= s ^ (s >> 26); // c
            b = t;
            return t + s;
        }
    } // namespace detail

    struct xorshift128
    {
        uint64_t a, b;

        xorshift128()
        {
            std::random_device rd{};
            a = (uint64_t(rd()) << 32) ^ rd();
            b = (uint64_t(rd()) << 32) ^ rd();
        }

        uint64_t operator()()
        {
            return detail::xorshift128p(a, b);
        }
    };

    struct xorshift128_bool : public xorshift128
    {
        uint64_t cur{};
        uint8_t bit;

        xorshift128_bool()
            : xorshift128(),
              bit(63)
        {
        }

        bool operator()()
        {
            if (bit >= 63)
            {
                cur = detail::xorshift128p(a, b);
                bit = 0;
                return cur & 1;
            }
            else
            {
                ++bit;
                return cur & (uint64_t(1) << bit);
            }
        }
    };
}
